﻿using GameStructure.Contracts;
using GameStructure.GaneState;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GameStructure.Test
{
    public class MockState : IGameObject
    {
        public bool HasUpdateBeenCalled { get; private set; }
        public bool HasRenderBeenCalled { get; private set; }

        public MockState()
        {
            HasUpdateBeenCalled = false;
            HasRenderBeenCalled = false;
        }

        public void Update(double elapsedTime)
        {
            HasUpdateBeenCalled = true;
        }

        public void Render()
        {
            HasRenderBeenCalled = true;
        }
    }

    [TestClass]
    public class Test_StateSystem
    {
        private StateSystem _stateSystem;
        private MockState _mock;

        /// <summary>
        ///Initialize() is called once during test execution before
        ///test methods in this test class are executed.
        ///</summary>
        [TestInitialize]
        public void Initialize()
        {
            _stateSystem = new StateSystem();
            _mock = new MockState();
        }

        /// <summary>
        ///Cleanup() is called once during test execution after
        ///test methods in this class have executed unless
        ///this test class' Initialize() method throws an exception.
        ///</summary>
        [TestCleanup]
        public void Cleanup()
        {
            //  TODO: Add test cleanup code
        }

        [TestMethod]
        public void TestAddedStateExists()
        {
            _stateSystem.AddState("splash", new SplashScreenState(_stateSystem));

            // Does the added function now exist?
            Assert.IsTrue(_stateSystem.Exists("splash"));
        }

        [TestMethod]
        public void TestMostRecentlyAddedStateExists()
        {
            _stateSystem.AddState("splash1", new SplashScreenState(_stateSystem));
            _stateSystem.AddState("splash2", new SplashScreenState(_stateSystem));

            Assert.IsTrue(_stateSystem.Exists("splash2"));
        }

        [TestMethod]
        public void TestFirstAddedStateExists()
        {
            _stateSystem.AddState("splash1", new SplashScreenState(_stateSystem));
            _stateSystem.AddState("splash2", new SplashScreenState(_stateSystem));

            Assert.IsTrue(_stateSystem.Exists("splash1"));
        }

        [TestMethod]
        public void TestChangeState()
        {
            _stateSystem.AddState("splash", new SplashScreenState(_stateSystem));
            _stateSystem.ChangeState("splash");

            Assert.IsInstanceOfType(_stateSystem.CurrentState, typeof(SplashScreenState));
        }

        [TestMethod]
        public void TestRenderSelectedState()
        {
            _stateSystem.AddState("mock", _mock);
            _stateSystem.ChangeState("mock");
            _stateSystem.Render();

            Assert.IsTrue(_mock.HasRenderBeenCalled);
        }

        [TestMethod]
        public void TestUpdateSelectedState()
        {
            _stateSystem.AddState("mock", _mock);
            _stateSystem.ChangeState("mock");
            _stateSystem.Update(0);

            Assert.IsTrue(_mock.HasUpdateBeenCalled);
        }

        [TestMethod]
        public void TestRenderNoSelectedState()
        {
            _stateSystem.AddState("mock", _mock);
            _stateSystem.Render();
            
            Assert.IsFalse(_mock.HasRenderBeenCalled);
        }

        [TestMethod]
        public void TestChangeStates()
        {
            var mock2 = new MockState();
            _stateSystem.AddState("mock1", _mock);
            _stateSystem.AddState("mock2", mock2);
            _stateSystem.ChangeState("mock1");
            _stateSystem.ChangeState("mock2");
            _stateSystem.Render();
            
            Assert.IsFalse(_mock.HasRenderBeenCalled);
            Assert.IsTrue(mock2.HasRenderBeenCalled);
        }
    }
}
